#include "raytracer.h"
#include "PerlinNoise.h"
/*-<==>-----------------------------------------------------------------
/ /CMaterial turbulence.
/----------------------------------------------------------------------*/
SCALAR CMaterial::getTurbulenceAddition(const VECTOR &loc, SCALAR aturbulence_factor) const
{
	SCALAR turbulenceValue = 0.0;
	SCALAR tmpTurbulence = aturbulence_factor;
	while(tmpTurbulence >= 1)
	{
		turbulenceValue += noise(loc.x/tmpTurbulence, loc.y/tmpTurbulence, loc.z/tmpTurbulence)*tmpTurbulence;
		tmpTurbulence /= 2.0;
	}
	return(128.0*turbulenceValue/aturbulence_factor);
}
void CMaterial::bumpNormal(const VECTOR & loc, VECTOR& normal) 
{
	SCALAR noiseCoefx = 
		(SCALAR)(noise((SCALAR)loc.x, 
		(SCALAR)loc.y,
		(SCALAR)loc.z));
	SCALAR noiseCoefy = 
		SCALAR(noise((SCALAR)(loc.y), 
		(SCALAR)(loc.z),
		(SCALAR)(loc.x)));
	SCALAR noiseCoefz = 
		SCALAR(noise((SCALAR)(loc.z), 
		(SCALAR)(loc.x),
		(SCALAR)(loc.y)));

	normal.x = (1.0f - bump_factor ) 
		* normal.x + bump_factor * noiseCoefx; 
	normal.y = (1.0f - bump_factor ) 
		* normal.y + bump_factor * noiseCoefy; 
	normal.z = (1.0f - bump_factor ) 
		* normal.z + bump_factor * noiseCoefz; 

}
void CMaterial::setBumpMap(bool value, SCALAR abump_factor)
{
	bump_factor = abump_factor;
	bumpMap = value;
}
/*-<==>-----------------------------------------------------------------
/ /CSolidMaterial 
/----------------------------------------------------------------------*/
CSolidMaterial::CSolidMaterial(const COLOR &diffuse, SCALAR reflectance, SCALAR refraction, SCALAR refractionI, SCALAR aambiental_factor,SCALAR adiffuse_factor,SCALAR aspecular_factor) {
	diffuse_color = diffuse;
	reflectance_factor = reflectance;
	refraction_factor = refraction;
	refraction_index = refractionI;
	diffuse_factor= adiffuse_factor;
	ambiental_factor=aambiental_factor;
	specular_factor=aspecular_factor;
	bumpMap = false;
}

/*-<==>-----------------------------------------------------------------
/CSolidMaterial 
/parameters are independent of the position
/----------------------------------------------------------------------*/
COLOR  CSolidMaterial::getDiffuseColor(const VECTOR &loc) const { 
	COLOR color = diffuse_color;

	return color; 
}


SCALAR CSolidMaterial::getReflectance(const VECTOR &loc)  const { 
	return reflectance_factor; 
}

SCALAR CSolidMaterial::getRefractionFactor(const VECTOR &loc)  const { 
	return refraction_factor; 
}
SCALAR CSolidMaterial::getRefractionIndex(const VECTOR &loc)  const { 
	return refraction_index; 
}


SCALAR CSolidMaterial::getDiffuseFactor(const VECTOR &loc)  const { 
	return diffuse_factor; 
}
SCALAR CSolidMaterial::getAmbientalFactor(const VECTOR &loc)  const { 
	return ambiental_factor; 
}
SCALAR CSolidMaterial::getSpecularFactor(const VECTOR &loc)  const { 
	return specular_factor; 
}

/*-<==>-----------------------------------------------------------------
/ /CSolidMaterial 
/----------------------------------------------------------------------*/
CCheckerMaterial::CCheckerMaterial (CMaterial *white, CMaterial *black, SCALAR size) {
	tile_black = black;
	tile_white = white;
	side_size = size;
}

/*-<==>-----------------------------------------------------------------
/	CCheckerMaterial  
/parameters are dependent of the position
/----------------------------------------------------------------------*/
bool CCheckerMaterial::is_white(const VECTOR &loc) const{

	int x= (int)abs((loc.x+SMALL_AMOUNT) / side_size);
	int y= (int)abs((loc.y+SMALL_AMOUNT) / side_size);
	int z= (int)abs((loc.z+SMALL_AMOUNT) / side_size);
	bool dec;
	dec = (x%2 == y%2)? true : false;
	dec = (z%2)? !dec : dec;
	int deci=(int)(loc.x*loc.y*loc.z);
	return (deci<0)? !dec : dec;
}

COLOR  CCheckerMaterial::getDiffuseColor(const VECTOR &loc) const { 
	COLOR color = (is_white(loc)? tile_white->getDiffuseColor(loc) : tile_black->getDiffuseColor(loc)) ;

	return color;

}

SCALAR CCheckerMaterial::getReflectance(const VECTOR &loc)  const { 
	return (is_white(loc)? tile_white->getReflectance(loc) : tile_black->getReflectance(loc));
}

SCALAR CCheckerMaterial::getRefractionFactor(const VECTOR &loc)  const { 
	return is_white(loc)? tile_white->getRefractionFactor(loc) : tile_black->getRefractionFactor(loc) ;
}
SCALAR CCheckerMaterial::getRefractionIndex(const VECTOR &loc)  const { 
	return is_white(loc)? tile_white->getRefractionIndex(loc) : tile_black->getRefractionIndex(loc) ;
}

SCALAR CCheckerMaterial::getDiffuseFactor(const VECTOR &loc)  const { 
	return is_white(loc)? tile_white->getDiffuseFactor(loc) : tile_black->getDiffuseFactor(loc) ;
}

SCALAR CCheckerMaterial::getAmbientalFactor(const VECTOR &loc)  const { 
	return is_white(loc)? tile_white->getAmbientalFactor(loc) : tile_black->getAmbientalFactor(loc) ;
}

SCALAR CCheckerMaterial::getSpecularFactor(const VECTOR &loc)  const { 
	return is_white(loc)? tile_white->getSpecularFactor(loc) : tile_black->getSpecularFactor(loc) ;
}
/*-<==>-----------------------------------------------------------------
/	CTurbulence  
/parameters are dependent of the position
/----------------------------------------------------------------------*/
CTurbMaterial::CTurbMaterial (const COLOR &diffuse, SCALAR reflectance,SCALAR refraction,SCALAR refractionI,  SCALAR aambiental_factor,SCALAR adiffuse_factor,SCALAR aspecular_factor, float aturbulence_factor)
:CSolidMaterial(diffuse, reflectance,refraction,refractionI,aambiental_factor,adiffuse_factor,aspecular_factor)
{
	turbulence_factor = aturbulence_factor;
}

COLOR  CTurbMaterial::getDiffuseColor(const VECTOR &loc) const
{
	SCALAR turbulence = abs(getTurbulenceAddition(loc, turbulence_factor)/256);
	COLOR color = diffuse_color;
	return color*COLOR(turbulence,turbulence,turbulence);
}
/*-<==>-----------------------------------------------------------------
/	CMarble
/
/----------------------------------------------------------------------*/
CMarbleMaterial::CMarbleMaterial (const COLOR &diffuse, SCALAR reflectance,SCALAR refraction,SCALAR refractionI,  SCALAR aambiental_factor,SCALAR adiffuse_factor,SCALAR aspecular_factor)
:CSolidMaterial(diffuse, reflectance,refraction,refractionI,aambiental_factor,adiffuse_factor,aspecular_factor)
{

}
void CMarbleMaterial::setMarbleParams(SCALAR aperiod, SCALAR apower, SCALAR aturb_size)
{
	period = aperiod;
	power = apower;
	turb_size = aturb_size;
}
COLOR  CMarbleMaterial::getDiffuseColor(const VECTOR &loc) const
{
	SCALAR marbleVal = loc.x*period + loc.y*period + loc.z*period + power*getTurbulenceAddition(loc,turb_size)/256.0; 
	SCALAR value = abs(sin(marbleVal));
	COLOR color = diffuse_color;
	return color*COLOR(value,value,value);
}
/*-<==>-----------------------------------------------------------------
/	CWood  
/
/----------------------------------------------------------------------*/
CWoodMaterial::CWoodMaterial (const COLOR &diffuse, SCALAR reflectance,SCALAR refraction,SCALAR refractionI,  SCALAR aambiental_factor,SCALAR adiffuse_factor,SCALAR aspecular_factor)
:CSolidMaterial(diffuse, reflectance,refraction,refractionI,aambiental_factor,adiffuse_factor,aspecular_factor)
{

}
COLOR  CWoodMaterial::getDiffuseColor(const VECTOR &loc) const
{
	//turbSize ha de ser baix.
	//SCALAR woodVal = abs(sin(sqrt(loc.x*loc.x + loc.y*loc.y + loc.z*loc.z + 0.4*getTurbulenceAddition(loc, turb_size)))); 
	SCALAR woodVal = abs(noise(loc.x/turb_size, loc.y/turb_size, loc.z/turb_size))*15;
	SCALAR grain = woodVal - int(woodVal);
	COLOR color = diffuse_color;
	return color*COLOR(grain,grain,grain);
}
void CWoodMaterial::setWoodParams(SCALAR aturb_size)
{
	turb_size = aturb_size;
}